Android之AlarmManagerService源码分析

AlarmManager实质上是一个全局定时器,是Android中常用的一种系统服务级别的提示服务,在指定时间或周期性启动其他组件(Activity、Service、BroadcastReceiver)。

之前有篇文章专门介绍了AlarmManager定时器的使用方法,获取到服务后,调用该服务的一些设置方法,在设定时间到达后就会启动指定的组件。

AlarmManagerService和JobSchedulerService一样都是系统服务,故它们的启动流程也类似,先看下时序图:

Zygote进程启动后会启动System进程,在System进程启动过程中会启动系统中的关键服务,如AMS、PMS、JobSchedulerService以及这里要分析的AlarmManagerService。

SystemServer启动AlarmManagerService服务调用的是SystemServiceManager类的startService方法:

private void startOtherServices() {
    try {
        . . .
        mSystemServiceManager.startService(AlarmManagerService.class);
        . . .
    } catch (RuntimeException e) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting core service", e);
    }
}


SystemServiceManager类的startService方法在JobSchedulerService的分析中已经分析过,这里粘贴写代码:

private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();

/**
 * 创建并启动一个继承自SystemService类的系统服务。
 *
 * @param 一个继承自SystemService类的服务类 
 * @return 服务类的实例
 * @throws 如果服务启动失败则抛RuntimeException异常
 */
@SuppressWarnings("unchecked")
public <T extends SystemService> T startService(Class<T> serviceClass) {
    // 获取服务类的类名
    final String name = serviceClass.getName();
    Slog.i(TAG, "Starting " + name);

    // 判断服务类是否是SystemService的子类
    if (!SystemService.class.isAssignableFrom(serviceClass)) {
        throw new RuntimeException("Failed to create " + name
                + ": service must extend " + SystemService.class.getName());
    }
    final T service;
    try {
        // 获取服务类包含一个Context参数的构造方法
        Constructor<T> constructor = serviceClass.getConstructor(Context.class);
        // 创建这个服务类的实例
        service = constructor.newInstance(mContext);
    } catch (InstantiationException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service could not be instantiated", ex);
    } catch (IllegalAccessException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service must have a public constructor with a Context argument", ex);
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service must have a public constructor with a Context argument", ex);
    } catch (InvocationTargetException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service constructor threw an exception", ex);
    }

    // 把服务添加到mServices列表中,方便后续使用时取出
    mServices.add(service);

    try {
        // 回调服务的onStart方法
        service.onStart();
    } catch (RuntimeException ex) {
        throw new RuntimeException("Failed to start service " + name
                + ": onStart threw an exception", ex);
    }
    return service;
}


在开启AlarmManagerService服务时,会创建服务的实例,看下该服务的创建过程:

final AlarmHandler mHandler = new AlarmHandler();
final Constants mConstants;

public AlarmManagerService(Context context) {
    super(context);
	// 初始化Handler和常量Constants类
    mConstants = new Constants(mHandler);
}


初始化Handler的代码后面调用时再分析,这里先看下常量类的实现:

/**
 * 该类中所有的时间单位都是毫秒。
 * 这些常量保持与系统全局设置一致。
 * 任何访问该类或该类中的字段都要持有AlarmManagerService.mLock锁
 */
private final class Constants extends ContentObserver {
    // 在设置中保存的键值
    private static final String KEY_MIN_FUTURITY = "min_futurity";
    private static final String KEY_MIN_INTERVAL = "min_interval";
    private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
    private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
    private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
            = "allow_while_idle_whitelist_duration";

    private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
    private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
    private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
    private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
    private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;

    // Minimum futurity of a new alarm
    public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;

    // Minimum alarm recurrence interval
    public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;

    // 从系统非空闲状态到可以执行flag为ALLOW_WHILE_IDLE的alarm的最小时间间隔:5秒钟
    public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;

    // 从系统空闲状态到可以执行flag为ALLOW_WHILE_IDLE的alarm的最小时间间隔:9分钟
    public long ALLOW_WHILE_IDLE_LONG_TIME = DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME;

    // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
    public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
            = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;

    private ContentResolver mResolver;
    private final KeyValueListParser mParser = new KeyValueListParser(',');
    private long mLastAllowWhileIdleWhitelistDuration = -1;

    public Constants(Handler handler) {
        super(handler);
        // 更新可以开始执行flag为ALLOW_WHILE_IDLE的alarm的最小时间间隔
        updateAllowWhileIdleMinTimeLocked();
        updateAllowWhileIdleWhitelistDurationLocked();
    }

	// 系统启动后会调用该方法,注册数据库监听
    public void start(ContentResolver resolver) {
        mResolver = resolver;
        // 监听数据库变化
        mResolver.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.ALARM_MANAGER_CONSTANTS), false, this);
        updateConstants();
    }

    // 更新可以开始执行flag为ALLOW_WHILE_IDLE的alarm的最小时间间隔
    public void updateAllowWhileIdleMinTimeLocked() {
        mAllowWhileIdleMinTime = mPendingIdleUntil != null
                ? ALLOW_WHILE_IDLE_LONG_TIME : ALLOW_WHILE_IDLE_SHORT_TIME;
    }

    public void updateAllowWhileIdleWhitelistDurationLocked() {
        if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
            mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
            BroadcastOptions opts = BroadcastOptions.makeBasic();
            opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
            mIdleOptions = opts.toBundle();
        }
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        // 数据库内容变化时,更新一些本地变量
        updateConstants();
    }
	. . .
}


看下AlarmManagerService类的onStart方法:

long mNativeData;
// 下一个包含wakeup的batch的start时间
private long mNextWakeup;
// 下一个包含Rtc wakeup的batch的start时间
private long mNextRtcWakeup;
// 下一个非wakeup的batch的start时间
private long mNextNonWakeup;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
PowerManager.WakeLock mWakeLock;
// 时间变化发送者
PendingIntent mTimeTickSender;
// 日期变化发送者
PendingIntent mDateChangeSender;
// 时间变化的广播接收者
ClockReceiver mClockReceiver;
// 监听息屏/亮屏的广播接收者
InteractiveStateReceiver mInteractiveStateReceiver;
// 监听卸载的广播接收者
private UninstallReceiver mUninstallReceiver;

@Override
public void onStart() {
    // 通过JNI对mNativeData进行初始化操作:
    // 打开设备驱动"/dev/alarm"返回一个long型的与fd文件描述符有关的值
    mNativeData = init();
    mNextWakeup = mNextRtcWakeup = mNextNonWakeup = 0;

    // 把当前时区信息保存到内核
    setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));

    // 初始化wakelock
    PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");

    // 初始化时间变化的广播发送者,因为这里的接收者是ALL,所以所有的应用程序收到的ACTION_TIME_TICK广播都是这里发送的
    mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
            new Intent(Intent.ACTION_TIME_TICK).addFlags(
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY
                    | Intent.FLAG_RECEIVER_FOREGROUND), 0,
                    UserHandle.ALL);
    // 初始化mDateChangeSender
    Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
    
    // 初始化驱动来调度alarm
    // 注册时间/日期变化广播接收者
    mClockReceiver = new ClockReceiver();
    // 调度时间变化事件,第一次发送时间变化广播,实现:在当前时间延迟一分钟后发送
    // 在ClockReceiver的onReceive方法中接收到时间变化的广博就会调用
    // scheduleTimeTickEvent方法发送一分钟后的广播
    mClockReceiver.scheduleTimeTickEvent();
    // 调度日期变化事件,每天0点发送一次广播
    mClockReceiver.scheduleDateChangedEvent();
    // 初始化监听息屏/亮屏的广播接收者
    mInteractiveStateReceiver = new InteractiveStateReceiver();
    // 监听应用卸载和SD卡不可用的广播,判断是否移除alarm
    mUninstallReceiver = new UninstallReceiver();
    
    if (mNativeData != 0) {
	    // 初始化AlarmThread并运行它的run方法
        AlarmThread waitThread = new AlarmThread();
        waitThread.start();
    } else {
        Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
    }

    // 发布服务
    publishBinderService(Context.ALARM_SERVICE, mService);
}

private native long init();

// 把当前时区保存到内核
void setTimeZoneImpl(String tz) {
    if (TextUtils.isEmpty(tz)) {
        return;
    }

    // 获取要保存的时区
    TimeZone zone = TimeZone.getTimeZone(tz);
    // 写入时区时,避免同时写
    boolean timeZoneWasChanged = false;
    synchronized (this) {
        // 再次获取要保存的时区
        String current = SystemProperties.get(TIMEZONE_PROPERTY);
        // 如果两次获取的时区不一致则说明时区发生变化,并重新把当前时区保存到设置中
        if (current == null || !current.equals(zone.getID())) {
            if (localLOGV) {
                Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
            }
            timeZoneWasChanged = true;
            SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
        }

        // Update the kernel timezone information
        // Kernel tracks time offsets as 'minutes west of GMT'
        // 更新内核时区信息
        // 内核跟踪时间偏移为GMT以西的分钟数
        int gmtOffset = zone.getOffset(System.currentTimeMillis());
        // 保存时区到内核
        setKernelTimezone(mNativeData, -(gmtOffset / 60000));
    }

    TimeZone.setDefault(null);

    // 如果时区发生改变则发送时区改变的广播
    if (timeZoneWasChanged) {
        Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra("time-zone", zone.getID());
        getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
    }
}

// 保存时区信息到内核中
private native int setKernelTimezone(long nativeData, int minuteswest);

// 时间/日期变化广播接收者
class ClockReceiver extends BroadcastReceiver {
    public ClockReceiver() {
        // 注册时间/日期变化的广播接收者
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_DATE_CHANGED);
        getContext().registerReceiver(this, filter);
    }
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
            if (DEBUG_BATCH) {
                Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
            }
            // 收到时间变化的广播,发送时间变化的广播给所有的接收者
            scheduleTimeTickEvent();
        } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
            // 由于内核不跟踪DST时间,所以需要在每天开始时,基于当前时区的gmt偏移 + 用户空间跟踪保存的信息来重置时区信息
            TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
            int gmtOffset = zone.getOffset(System.currentTimeMillis());
            // 日期变化时更新内核保存的时区信息
            setKernelTimezone(mNativeData, -(gmtOffset / 60000));
            // 调度日期变化事件
            scheduleDateChangedEvent();
        }
    }
    
    // 一分钟后给所有接收ACTION_TIME_TICK广播的接收者发送广播
    public void scheduleTimeTickEvent() {
        final long currentTime = System.currentTimeMillis();
        final long nextTime = 60000 * ((currentTime / 60000) + 1);

        // 下一次执行事件的延迟时间
        final long tickEventDelay = nextTime - currentTime;

        final WorkSource workSource = null; // Let system take blame for time tick events.
        // 调用setImpl方法设置给所有接收ACTION_TIME_TICK广播的接收者发送广播
        setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
                0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null,
                Process.myUid());
    }

    // 调度日期变化事件
    public void scheduleDateChangedEvent() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);

        final WorkSource workSource = null; // Let system take blame for date change events.
        // 设置mDateChangeSender
        setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender,
                AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid());
    }
}

// 修正参数并设置传递过来的PendingIntent
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
        PendingIntent operation, int flags, WorkSource workSource,
        AlarmManager.AlarmClockInfo alarmClock, int callingUid) {
    if (operation == null) {
        Slog.w(TAG, "set/setRepeating ignored because there is no intent");
        return;
    }

    /*下面要修正传递过来的PendingIntent各相关参数了,所以可以在这里增加对alarm的管控规则*/

    // 检查时间窗长度,如果超过半天就转换成一小时。
    if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
        Slog.w(TAG, "Window length " + windowLength
                + "ms suspiciously long; limiting to 1 hour");
        windowLength = AlarmManager.INTERVAL_HOUR;
    }

    // 检查alarm的执行周期,如果alarm的执行周期小于1分钟,则转换为1分钟。
    // 从这里可以得知周期性alarm的最小有效周期为1分钟,若要执行周期小于1分钟的事,不能使用alarm实现
    final long minInterval = mConstants.MIN_INTERVAL;// 1分钟
    if (interval > 0 && interval < minInterval) {
        Slog.w(TAG, "Suspiciously short interval " + interval
                + " millis; expanding to " + (minInterval/1000)
                + " seconds");
        interval = minInterval;
    }

    // 检查type的合法性
    if (type < RTC_WAKEUP || type > RTC_POWEROFF_WAKEUP) {
        throw n
  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值